#include <cassert>
#include <algorithm>

#include "DelaunayTri.h"
#include "data_helper.hpp"
#include "string_math.hpp"
#include "string_utils.h"
#include "string_intf.h"


StringIntf::StringIntf(const std::vector<std::string>& lines, const bool shift/*  = true */, const bool normalize/*  = true */)
{
    std::vector<String3DPoint> str_points = LoadData(lines);
    PreprocessInfo preprocess_info = GrabPreprocessInfo(str_points);

    if (!shift) {
        preprocess_info.shift = ineffective_shift;
    }
    if (!normalize) {
        preprocess_info.scale = ineffective_scale;
    }

    preprocessed_points_info = PreprocessPoints(
        str_points,
        preprocess_info
    );
}


TriangleWithPlane::TriangleWithPlane(const BaseGeo::Triangle& triangle)
    : plane(triangle), Triangle(triangle) 
{}


void StringIntf::Triangulation()
{
    BaseGeo::DelaunayTri delaunay(preprocessed_points_info.points);

    auto triangle_list = delaunay.Triangulation();

    for (BaseGeo::Triangle& triangle : triangle_list) {
        triangles.push_back(
            TriangleWithPlane(triangle)
        );
    }

#if NEED_TO_SORT_TRIANGLES
    // TODO
    // sort(triangle_list)
#endif
}


std::string StringIntf::QueryZ(const std::string& coord_string)
{
    std::vector<std::string> splits = SplitString(coord_string, ',');
    if (splits.size() != 2) {
        return std::string(INVALID_2D_COORDINATE_IN_QUERYING);
    }

    String3DPoint str_query_point = String3DPoint(coord_string);
    BaseGeo::Point query_point = PreprocessPoint(
        str_query_point,
        preprocessed_points_info.preprocess_info
    );
    auto found = std::find_if(triangles.begin(), triangles.end(),
        [&](const TriangleWithPlane& triangle){ return triangle.IsPointInThis(query_point); }
    );

    if (found == triangles.end()) {
        return std::string(INVALID_2D_COORDINATE_IN_QUERYING);
    }

    query_point.z = found->plane.QueryZ(query_point.x, query_point.y);
    String3DPoint str_queried_point = InversePreprocessedPoint(
        query_point,
        preprocessed_points_info.preprocess_info
    );

    return str_queried_point.z.ToString();
}